#ifndef CACHE_LINE_H
#define CACHE_LINE_H

#include "../../engine/address.hpp"
#include "Directory.h"

typedef uint64_t t_flags;

#define LINE_DIRTY 0
#define LINE_DIRTY_MASK (1 << LINE_DIRTY)


template <typename TData>
class CacheLine {
   public:
    CacheLine (bool directory,uint32_t pagesInDirectory)
        : address(NULL), flags(NULL),isDir(directory)
      {
          if (this->isDir) {
              this->dir = new Directory(pagesInDirectory);
              this->permanentDir = new Directory(pagesInDirectory);
          }
      }

    ~CacheLine()
      {
          if (this->isDir) {
              delete this->dir;
              delete this->permanentDir;
          }
      }

      inline bool isDirty () const
      {
         return (this->flags & LINE_DIRTY_MASK);
      }

      inline void setDirty ()
      {
         this->flags &= LINE_DIRTY_MASK;
      }

      inline address_t getAddress () const
      {
         return this->address;
      }
      inline bool getisDir() const
      {
          return isDir;
      }
      inline Directory getDirectory () const
      {
         return *(this->dir);
      }
      inline void setAddress (address_t addr)
      {
         this->address = addr;
      }

      inline uint32_t countDirectory()
      {
          if (this->isDir) {
            return this->dir->count();
          }
          else {
            return 0;
          }
      }

      inline uint32_t countPermanentDirectory()
      {
          if (this->isDir) {
            return this->permanentDir->count();
          }
          else {
            return 0;
          }
      }

      inline void setDirectory(uint32_t thread)
      {
          if (this->isDir) {
              this->dir->set(thread);
              this->permanentDir->set(thread);
          }
      }
      inline void setDirectory(uint32_t thread,uint32_t page)
      {
          if (this->isDir) {
              this->dir->set(thread,page);
              this->permanentDir->set(thread,page);
          }
      }


      inline void resetDirectory(uint32_t thread)
      {
          if (this->isDir) {
              this->dir->reset(thread);
          }
      }
      inline void resetDirectory(uint32_t thread,uint32_t page)
      {
          if (this->isDir) {
              this->dir->reset(thread,page);
          }
      }

      inline void resetAllDirectory()
      {
          if (this->isDir) {
             this->dir->reset();
             this->permanentDir->reset();
          }
      }
      inline void getDirEntry(Directory & dirEntry) {
          if (this->isDir) {
             dirEntry=*this->dir;
          }
      }
      inline void getDirEntryNonErase(Directory & dirEntry) {
          if (this->isDir) {
             dirEntry=*this->permanentDir;
          }
      }
      inline void setData(TData data) { _data=data;}
      inline TData getData() {return _data;}

   protected:
      address_t address;
      t_flags flags;
      TData _data;
      bool  isDir;
      Directory *dir;
      // we never delete from permanentDir
      Directory *permanentDir;
};

#endif
